package remotecall.streamacceptor;

import android.os.IBinder;
import android.os.Parcel;
import android.os.Parcelable;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

import com.zjw.zy.utils.GsonUtils;
import com.zjw.zy.utils.LogUtils;

import java.lang.reflect.Type;

import remotecall.utils.RemoteUtils;


/**
 * @author ：zhong.jw
 * @date ：Created in 2022/6/23 10:21
 * 远程回调结果
 */
public class RemoteCallResult implements Parcelable {

    private static final String TAG = "RemoteCallResult";

    /**
     * 空指
     */
    public static final int TYPE_NULL = -1;
    /**
     * 普通bean
     */
    public static final int TYPE_COMMON = 1;
    /**
     * parcelable bean
     */
    public static final int TYPE_PARCELABLE = 2;
    /**
     * 大数据 bean
     */
    public static final int TYPE_CACHE = 3;

    private int mTypeFlag = TYPE_COMMON;

    private final String mClassName;
    private String mJson;
    private IBinder mStrongBinder;
    private String mBinderDescriptor;
    //如果是 Parcelable 直接传递， 不走json;
    private Parcelable mParcelable;
    //如果是大数据，走分包
    private String mCacheKey;
    private Object mCacheObj;

    public RemoteCallResult(@Nullable Object obj) {
        if (obj == null) {
            mTypeFlag = TYPE_NULL;
            mCacheObj = "";
            mClassName = "";
            mStrongBinder = null;
            return;
        }
        mCacheObj = obj;
        mClassName = obj.getClass().getName();
        String key = String.valueOf(System.identityHashCode(obj));
        //先检测是否缓存
        if (StreamAcceptor.openInService().cacheBytesIfNeed(key, obj)) {
            mTypeFlag = TYPE_CACHE;
            mBinderDescriptor = RemoteUtils.createDefaultDescriptor() + "@RemoteCallResult";
            mStrongBinder = new ByteBinderImpl(mBinderDescriptor);
            mCacheKey = key;
        } else if (obj instanceof Parcelable) {
            mTypeFlag = TYPE_PARCELABLE;
            mParcelable = (Parcelable) obj;
        } else {
            mTypeFlag = TYPE_COMMON;
            mJson = GsonUtils.toJson(obj);
        }
    }

    protected RemoteCallResult(Parcel in) {
        mTypeFlag = in.readInt();
        mClassName = in.readString();
        if (mTypeFlag == TYPE_NULL) {
            mCacheObj = null;
            return;
        }
        if (mTypeFlag == TYPE_PARCELABLE) {
            mParcelable = in.readParcelable(getClassLoader());
            return;
        }
        if (mTypeFlag == TYPE_CACHE) {
            mCacheKey = in.readString();
            mStrongBinder = in.readStrongBinder();
            mBinderDescriptor = in.readString();
            StreamAcceptor acceptor = StreamAcceptor.open(mStrongBinder, mBinderDescriptor);
            try {
                mCacheObj = acceptor.acceptBytes(mCacheKey, getClazz());
                mJson = acceptor.getJson();
            } finally {
                acceptor.recycler();
            }
            return;
        }
        if (mTypeFlag == TYPE_COMMON) {
            mJson = in.readString();
            mCacheObj = GsonUtils.fromJson(mJson, getClazz());
        }
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeInt(mTypeFlag);
        dest.writeString(mClassName);
        if (mTypeFlag == TYPE_PARCELABLE) {
            dest.writeParcelable(mParcelable, flags);
        }else if (mTypeFlag == TYPE_CACHE) {
            dest.writeString(mCacheKey);
            dest.writeStrongBinder(mStrongBinder);
            dest.writeString(mBinderDescriptor);
        }else{
            dest.writeString(mJson);
        }
    }

    @Override
    public int describeContents() {
        return 0;
    }

    public static final Creator<RemoteCallResult> CREATOR = new Creator<RemoteCallResult>() {
        @Override
        public RemoteCallResult createFromParcel(Parcel in) {
            return new RemoteCallResult(in);
        }

        @Override
        public RemoteCallResult[] newArray(int size) {
            return new RemoteCallResult[size];
        }
    };

    @SuppressWarnings("unchecked")
    public <RESULT> RESULT getResult() {
        //如果是 parcelable
        if (mTypeFlag == TYPE_PARCELABLE) {
            return (RESULT) mParcelable;
        }

        return (RESULT) mCacheObj;
    }

    @SuppressWarnings("unchecked")
    public <RESULT> RESULT getResult(Type type) {
        //如果是 parcelable
        if (mTypeFlag == TYPE_PARCELABLE) {
            return (RESULT) mParcelable;
        }

        return GsonUtils.fromJson(mJson, type);
    }

    private ClassLoader getClassLoader() {
        return getClazz().getClassLoader();
    }

    @NonNull
    private Class<?> getClazz() {
        try {
            return Class.forName(mClassName);
        } catch (ClassNotFoundException e) {
            LogUtils.e(TAG, e.getMessage());
        }
        throw new RuntimeException(TAG + "： can't find class " + mClassName);
    }
}
